<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
 * CodeIgniter
 *
 * An open source application development framework for PHP 4.3.2 or newer
 *
 * @package		CodeIgniter
 * @author		ExpressionEngine Dev Team
 * @copyright	Copyright (c) 2008 - 2009, EllisLab, Inc.
 * @license		http://codeigniter.com/user_guide/license.html
 * @link		http://codeigniter.com
 * @since		Version 1.0
 * @filesource
 */
// ------------------------------------------------------------------------

/**
 * @package		CodeIgniter
 * @subpackage	Libraries
 * @author		AnhDT
 */

class Fx_Block {

    protected $_env = array('Fx_Wrapper');

    /** Set Level */
    protected $level = 0;

    /** Output Buffer Level */
    protected $ob_level = 0;

    /** Singleton Patern */
    protected static $instance = NULL;

    /** id of block */
    protected  $id  = '';

    /** alias of block */
    protected $alias = '';

    /** name of block */
    protected $name = '';

    /** Output Buffer String */
    protected $output = '';

    /** File Template */
    protected $template = APP_TEMPLATE_DEFAULT;

    /** Container of This Block */
    protected $parent = NULL;

    /** Children Of Block */
    protected $children = array();

    /** Hidden Block */
    protected $visible = FALSE;

    /** Variables Of Block */
    protected $vars = array();

    /** Layout Of Block */
    protected $layout = NULL;

    /** Wrapper Of Block */
    protected $wrapper = NULL;

    /** Module */
    protected $module = '';

    /** Current Controller*/
    protected $controller = NULL;

    /** Tag Name*/
    protected $tagName = 'block';

    /** Param Tag Name*/
    protected $paramTagName = 'param';

    /** Js Files*/
    protected $_js = array();

    /** CSS Files*/
    protected $_css = array();

    /** Resource Files*/
    protected $_resources = array('images' => array(), 'video'  => array());

    /**  Current Layout*/
    protected $_layout = NULL;
	
	// Spec Components
	/** Loader */
	public $loader = NULL;
	
	/** Registry*/
	public $registry = NULL;
	
	/** theme*/
	protected $_theme = '';

    /** CONSTRUCT AND DESTRUCT */

    function __construct() {
		$this->_beforeInit()
        	 ->_init()
			 ->_afterInit();
    }

    function __destruct() {
    // reset properties
    }
    /** END HANDLER */
	
	/** Handler Event */
    public function _beforeInit() {
        foreach ($this->_env as $class) {
            if (!class_exists($class)) {
                require_once strtolower($class . EXT);
            }
        }
		return $this;
    }
	
	/** Handler Event */
    public function _afterInit() {
        return $this;
    }
	
	public function _init($id = '', $data = array()) {
        if (is_string($id) && $id) {
            $this->id = $id;
        }
        $this->setData($data);
        $this->ob_level = ob_get_level();
		$CI = & get_instance();
		// Loader
		$this->loader = & $CI;
		// Registry
		$CI->load->library('cacher/st_registry');
		$this->registry = & $CI->st_registry;
		// Theme
		if (!class_exists('Fx_Path'))
		{
			$CI->load->library('doc/fx_path');
		}
		$this->_theme = Fx_Path::getCurrentTheme();
		return $this;
	}

    /** MAGIC FUNCTION */
    function __set($property, $value) {
        $this->vars[$property] = $value;
        return $this;
    }

    function __get($property) {
        if (array_key_exists($property, $this->vars)) {
            return $this->vars[$property];
        }
        return false;
    }

    function __isset($property) {
        return isset($this->vars[$property]);
    }

    function __unset($property) {
        unset($this->vars[$property]);
        return $this;
    }

    function _call($method, $args) {
    //Implement Later
    }

    function __toString() {
        return (string)$this->getOutputHtml();
    }

    /** END HANDLER */

    /** SET ID OF BLOCK */
    public function setId($id) {
        if (is_string($id) || !empty($id)) {
            $this->id = $id;
        }
        return $this;
    }

    /** GET ID OF BLOCK */
    public function getId() {
        return (string)$this->id;
    }

    /** OB LEVEL HANDLER */
    public function getLevel() {
        return $this->ob_level;
    }
    /** END HANDLER */

    /** VISIBLE HANDLER */
    public function isVisible() {
        return $this->visible;
    }

    public function setVisible($visible = TRUE) {
        $this->visible = $visible;
        return $this;
    }
    /** END HANDLER */

    /** NAME  HANDLER */
    public function setName($name) {
        if (is_string($name))
            $this->name = $name;
        return $this;
    }

    public function getName() {
        return $this->name;
    }
    /** END HANDLER */

    /** ALIAS  HANDLER */
    public function setAlias($alias) {
        if (is_string($alias) || !empty($alias))
            $this->alias = $alias;
        return $this;
    }

    public function getAlias() {
        return (string)$this->alias;
    }
    /** END HANDLER */

    /** TAG NAME HANDLER */
    public function setTagName($tagName) {
        $this->tagName = $tagName;
        return $this;
    }

    public function getTagName() {
        return $this->tagName;
    }
    /** END HANDLER */

    /** PARAM TAG NAME HANDLER */
    public function setParamTagName($tagName) {
        $this->paramTagName = $tagName;
        return $this;
    }

    public function getParamTagName() {
        return $this->paramTagName;
    }
    /** END HANDLER */

    /** CONTROLLER HANDLER */
    public function setController(Controller $controller) {
        $this->controller = & $controller;
        return $this;
    }

    public function getController() {
        return $this->controller;
    }
    /** END HANDLER */

    /** HANDLER DATA */

    public function getDataCollection() {
        $properties = get_class_vars(get_class($this));
        if (count($properties) == 0) {
            return array();
        }
        $data = array();
        foreach (array_keys($properties) as $prop) {
            $data[$prop] = $this->$prop;
        }
        return $data;
    }

    public function getData($property) {
        if (!is_string($property) || !array_key_exists($property, get_class_vars(get_class($this)))) {
            return;
        }
        return $this->$property;
    }

    public function setData($data = array()) {
        if (!is_array($data) || count($data) == 0) {
            return;
        }
        $properties = get_class_vars(get_class($this));
        foreach ($data as $propName => $value) {
            switch ($propName) {
                case 'wrapper':
                    if ($value instanceof Fx_Wrapper)
                        $this->setWrapper($value);
                    break;
                case 'template':
                    $this->setTemplate($value);
                    break;
                case 'parent':
                    if ($value instanceof Fx_Block)
                        $this->setParent($value);
                    break;
                case 'children':
                    if (is_array($value) && count($value) > 0) {
                        $this->addChildren($value);
                    }
                    break;
                case 'layout':
                    if ($value instanceof FXLayout)
                        $this->layout = $value;
                    break;
                case 'vars':
                    if (is_array($value) && count($value) > 0) {
                        foreach ($value as $key => $vParam) {
                            $this->addVariable($key, $vParam);
                        }
                    }
                    break;
                default:
                    $this->$propName = $value;
                    break;
            }
        }
        return $this;
    }
    /** END HANDLER */

    /** SINGLETON PATTERN */

    public static function getInstance() {
        if (self::$instance === NULL) {
            self::$instance	= new self;
        }
        return self::$instance;
    }
    /** END HANDLER */

    /** PARRENT HANDLER */
    public function hasParent() {
        return ($this->getParent() != NULL);
    }

    public function getParent() {
        return $this->parent;
    }

    public function setParent($parentBlock = NULL) {
        if (!$parentBlock || !$parentBlock instanceof Fx_Block) {
            throw new Exception('Parent Of This Block NOT NULL and Must Be A Block');
        }
        $this->parent =& $parentBlock;
        $this->parent->appendChild($this);
        $this->level = $this->parent->getLevel() + 1;
        return $this;
    }

    /** END HANDLER */

    /** WRAPPER HANDLER */
    public function getWrapper() {
        return $this->wrapper;
    }

    public function setWrapper(Fx_Wrapper $wrapper) {
        if (!$wrapper) {
            return;
        }
        $this->wrapper = $wrapper;
        return $this;
    }
    /** END HANDLER */

    /** OUTPUT HANDLER */
    public function getOutputHtml($refresh = TRUE) {
        if (!$this->isVisible()) {
            return NULL;
        }
        if ($refresh) {
            $this->parseTemplate();
		}
        $output = $this->output;
        return $output;
    }
	
	/** OUTPUT HANDLER */
    public function outputHtml() {
        if (!$this->isVisible()) {
            return $this;
        }
        if (($wrapper = $this->getWrapper()) instanceof Fx_Wrapper) {
        // Handler Js, CSS Files
            $output = sprintf("%s%s%s%s%s", $this->getCssHtml(), $this->getJsHtml(),
                $wrapper->getStartWrapper(), $this->getOutputHtml(), $wrapper->getEndWrapper());
        } else {
            $output = sprintf("%s%s%s", $this->getCssHtml(), $this->getJsHtml(), $this->getOutputHtml());
        }
        if (isset($output)) {
            echo $output;
        }
        return $this;
    }
    /** END HANDLER */

    /** TEMPLATE HANDLER */

    public function hasTemplate() {
        return ($this->template != NULL	);
    }

    public function getTemplate() {
        return $this->template;
    }
	
	/**Set Template For Block To View After*/
    public function setTemplate($relativeFilePath, $parse = FALSE) {
		// Path: ./application/themes/[specific]/templates/
		// Spec: welcome/test.php
		$filePath = Fx_Path::getTemplatePath() . $relativeFilePath;
        if (!file_exists($filePath)) {
            throw new Exception('File "' . $filePath . '" Not Found');
        }
        $this->template = $filePath;
        if ($parse) {
            $this->parseTemplate();
        }
        return $this;
    }

    /** -- PARSER TEMPLATE HANDLER -- */
	/**Parse Template To Data*/
    public function parseTemplate($templateFile = NULL) {
        if ($templateFile) {
            $this->template  = $templateFile;
        } else {
            $templateFile = $this->template;
        }
        if (!$this->hasTemplate()) {
            return $this;
        }
        extract($this->getVars());
        ob_start();
        $tagName = $this->getTagName();
        $paramTagName = $this->getParamTagName();
        $contentHtml = file_get_contents($this->getTemplate());
        // Handler Variable
        $contentHtml = preg_replace("/<entity type=\"$paramTagName\"(.*)id=\"(.*)\"\/\>/",
            "<?php if (isset($$2)) echo $$2; ?>", $contentHtml);
		// Parser Constant
		$contentHtml = preg_replace("/{{(.*)}}/", "<?php echo $1?>", $contentHtml);
		// Parser Variable
        $contentHtml = str_replace("<?=", "<?php echo ", $contentHtml);
        $contentHtml = preg_replace("/;*\s*\?>/", "; ?>", $contentHtml);
        $contentHtml = eval("?>" . $contentHtml);
        // Output HTML Content To Buffer
		echo $contentHtml;
        //echo $this->getJsHtml() . $this->getCssHtml() . $contentHtml;
        // Return the file data if requested
        $this->output = ob_get_contents();
        @ob_end_clean();
        return $this;
    }

    /** -- END HANDLER -- */

    /** END HANDLER */

    /** VARIABLES HANDLER */
    public function getVars($merge = true) {
        $vars = & $this->vars;
        if (!$merge) {
            return $vars;
        }
        if ($this->hasChildren()) {
            $children = $this->getChildren();
            foreach ($children as $child) {
                $vars = array_merge($vars, $child->getVars());
            }
        }
        return $vars;
    }

    public function addVariable($key, $value) {
        $this->vars[$this->getId() . '_' . $key] = $value;
        return $this;
    }

    /** END HANDLER */

    /** CHILDREN HANDLER */

    public function appendChild($block, $overwrite = TRUE) {
        if (!$this->hasChild($block) || ($this->hasChild($block) && $overwrite)) {
            if (!$block->getParent() || $block->getParent()->getId() !== $this->getId()) {
                $block->setParent($this);
            }
            $this->children[$block->getId()] = $block;
        }
        return $this;
    }

    public function addChildren() {
        $num_args = func_num_args();
        if ($num_args == 0) {
            return;
        }
        $arg_list = func_get_args();
        foreach ($arg_list as $argurment) {
            if (is_array($argurment)) {
                foreach ($argurment as $block) {
                    $this->appendChild($block);
                }
            } else if ($argurment instanceof Fx_Block) {
                    $this->appendChild($argurment);
                }
        }
        return $this;
    }

    public function hasChild($block) {
        if ($block instanceof Fx_Block) {
            return array_key_exists($block->getId(), $this->getChildren());
        } else if (is_string($block)) {
                return array_key_exists($block, $this->getChildren());
            }
    }
	
	/**Get Block Child Of Parent Block*/
    public function getChild($id) {
        return (isset($this->children[$id])) ? $this->children[$id] : NULL;
    }
	
	/**Get Content Child Html Of Block */
    public function getChildHtml($id, $ignore_hidden = TRUE) {
        if (isset($this->children[$id])) {
            $block = $this->getChild($id);
            $visible = $block->isVisible();
            $output = '';
            if ($visible) {
                $output = $block->getOutputHtml();
            } else {
                if ($ignore_hidden) {
                    $block->setVisible(TRUE);
                    $output = $block->getOutputHtml();
                }
            }
            return $output;
        }
        return NULL;
    }
	
	/**Get Children For Block*/
    public function getChildren() {
        return $this->children;
    }
	
	/**Set Children For Block*/
    public function setChildren($children = array()) {
        if (!is_array($children)) {
            return $this;
        }
        $this->children = $children;
        return $this;
    }

    public function hasChildren() {
        return (!count($this->getChildren()) == 0);
    }
    /** END HANDLER */

    /**
     * CSS HANDLER
     * @param String (source file)
     */
    public function addCss($_css) {
		$_css = Fx_Path::getCssPath() . $_css;
        if (is_string($_css)) {
            if (!in_array($_css, $this->_css)) {
                $this->_css[$_css] = $_css;
            }
        } else if (is_array($_css)) {
                foreach ($_css as $file) {
                    $this->addCss($file);
                }
            }
        return $this;
    }

    public function removeCss($_css) {
        if (array_key_exists($_css, $this->_css)) {
            unset($this->_css[$_css]);
        }
        return $this;
    }

    public function getCssHtml() {
        if (count($this->_css) === 0) {
            return '';
        }
        $include = '';
        foreach ($this->_css as $css) {
            $include .= sprintf("<link rel=\"stylesheet\" type=\"text/css\" href=\"" . base_url() . "%s\" />\n", $css);
        }
        return $include;
    }

    public function clearCssCollection() {
        $this->_css = array();
        return $this;
    }

    public function getCssCollection() {
        return (array)$this->_css;

    }
    /** END HANDLER */

    /**
     * JAVASCRIPT HANDLER
     * @param String (source file)
     */
    public function addJs($_js) {
		$_js = Fx_Path::getJsPath() . $_js;
        if (is_string($_js)) {
            if (!in_array($_js, $this->_js)/* && file_exists($_js)*/) {
                $this->_js[$_js] = $_js;
            }
        } else if (is_array($_js)) {
                foreach ($_js as $_file) {
                    $this->addJs($_js);
                }
            }
        return $this;
    }

    public function removeJs($_js) {
        if (array_key_exists($_js, $this->_js)) {
            unset($this->_js[$_js]);
        }
        return $this;
    }

    public function getJsHtml() {
        if (count($this->_js) === 0) {
            return '';
        }
        $include = '';
        foreach ($this->_js as $_js) {
            $include .= sprintf("<script language=\"javascript\" src=\"".base_url()."%s\"/></script>\n", $_js);
        }
        return $include;
    }

    public function clearJsCollection() {
        $this->_js = array();
        return $this;
    }

    public function getJsCollection() {
        return (array)$this->_js;
    }
    /** END HANDLER */


    public function addResource($_type = 'image', $_res = NULL) {
        if (!is_string($_type) || empty($_type)) {
            return $this;
        }
        if (Fx_Resource::_isValidType($_type)) {
            if ($_res instanceof Fx_Resource && !empty($_res)) {
                $this->_resources[$_type][$_res->getKey()] = $_res;
            }
        }
        return $this;
    }

    public function getResource($type = 'image', $_key = '') {
        if (!is_string($_type) || !is_string($_key) || empty($_type) || empty($_key)) {
            return NULL;
        }
        if (!Fx_Resource::_isValidType($_type)) {
            return NULL;
        }
        return $this->_resources[$_type][$_key];
    }

    public function removeResource($_type = 'image', $_key = '') {
        if (!is_string($_type) || !is_string($_key) || empty($_type) || empty($_key)) {
            return $this;
        }
        if (!Fx_Resource::_isValidType($_type) || !$this->hasResouce($type, $_key)) {
            return $this;
        }
        unset($this->_resources[$_type][$_key]);
        return $this;
    }

    public function hasResouce($_type = 'image', $_key = '') {
        if (!is_string($_type) || !is_string($_key) || empty($_type) || empty($_key)) {
            return FALSE;
        }
        if (!Fx_Resource::_isValidType($_type)) {
            return FALSE;
        }
        if (!isset($this->_resources[$_type][$_key])) {
            return FALSE;
        }
        return TRUE;
    }

    public function emptyResources() {
        $this->_resources = array('image'=>array(), 'video'=>array());
        return $this;
    }


    public function getLayout() {
        return $this->_layout;
    }

    public function setLayout(Fx_Layout $new_layout) {
        if (!empty($new_layout)) {
            $this->_layout = $new_layout;
        }
        return $this;
    }

    public function getType() {
        return get_class($this);
    }

    public function countChildren() {
        return count($this->_children);
    }

    public function getBlockLevel() {
        return (int)$this->level;
    }
	
	public function getUrl($url)
	{
		if (!empty($url) && is_string($url))
		{
			return $this->getBaseUrl() . $url;		
		}
		return null;	
	}
	
	public function getBaseUrl()
	{
		return base_url();	
	}
}

/** UNIT TEST */
/*
error_reporting(E_ALL | E_STRICT);
$wrapper = new Fx_Wrapper('wrapper_test', '<div>', '</div>');
$wrapper->addClass('test');
$wrapper->addClass('test1');
$block   = new Fx_Block('block_test', array(
				     'wrapper' => $wrapper,
					 'alias'   => 'fx',
					 'name'    => 'block',
					 'vars'    => array('ab'=>'abc', 'bc'=>'bcf')
					 ));
$sub_block   = new Fx_Block('block_abc', array(
				     'wrapper' => $wrapper,
					 'alias'   => 'fx',
					 'name'    => 'block',
					 'vars'    => array('sub_ab'=>'sub_abc', 'sub_bc'=>'sub_bcf')
					 ));
$sub_block12   = new Fx_Block('block_abc23', array(
				     'wrapper' => $wrapper,
					 'alias'   => 'fx',
					 'name'    => 'block',
					 'vars'    => array('sub12_ab'=>'sub_abc', 'sub12_bc'=>'sub_bcf')
					 ));
$sub_block13   = new Fx_Block('block_abc13', array(
				     'wrapper' => $wrapper,
					 'alias'   => 'fx',
					 'name'    => 'block',
					 'vars'    => array('sub12_ab'=>'sub_abc', 'sub_bc'=>'sub_bcf')
					 ));
$sub_sub_block13   = new Fx_Block('block_abc135', array(
				     'wrapper' => $wrapper,
					 'alias'   => 'fx',
					 'parent'  => $sub_block12,
					 'name'    => 'block',
					 'vars'    => array('sub_ab'=>'sub_abc', 'sub_bc'=>'sub_bcf')
					 ));

$block->appendChild($sub_block);
$block->appendChild($sub_block12);
$block->appendChild($sub_block13);
//$block->outputHtml();
print_r ($block->getDataCollection());
echo $block->getData('id');
$block->var1 = 'abc';
print_r ($block->getData('vars'));
//echo $block->getTemplate();
print_r ($block->getVars());
print_r ($sub_block->getVars());
$block->addChildren($sub_block, $sub_block12, $sub_block13);
echo (count($block->getChildren()));

$block->addChildren($sub_block, $sub_block12, $sub_block13);
$sub_block->setTemplate('2columns.tpl');
$sub_block12->setTemplate('2columns.tpl');
$block->setTemplate('3columns.tpl');
print_r ($block->getVars());

echo count($block->getVars());

//echo $block->getOutputHtml();
$block->outputHtml();
*/